// Copyright Leonid Shlyapnikov 2010
// Distributed under the GNU Lesser General Public License http://www.gnu.org/licenses/lgpl.html
//    author: Leonid Shlyapnikov, created on: Feb 1, 2010

#include <boost/test/test_tools.hpp>
#include <sstream>
#include <cstring>

#include "../../main/fixparser/MemoryArea.h"
#include "../../main/fixparser/Field.h"
#include "../../main/fixparser/log.h"
#include "../../main/fixparser/util.h"

void Field_create() {
    using fixparser::MemoryArea;
    using fixparser::Field;
    using std::strlen;

    // GIVEN
    MemoryArea ma(1024);
    const char* TAG = "TAG";
    const char* VALUE = "VALUE";

    // WHEN
    Field* f0 = ma.create<Field>();
    BOOST_CHECK_EQUAL(true, f0->isEmpty());
    f0->setTag(TAG);
    f0->setValue(VALUE);

    // THEN
    BOOST_CHECK_EQUAL(false, f0->isEmpty());
    BOOST_CHECK_EQUAL(ma.size(), sizeof(Field) + strlen(TAG) + 1 + strlen(VALUE) + 1);  // +1 for terminating zero
    BOOST_CHECK_EQUAL(f0->tag().c_str(), TAG);
    BOOST_CHECK_EQUAL(f0->value().c_str(), VALUE);
}

void Field_resetTagShort() {
    using fixparser::MemoryArea;
    using fixparser::Field;
    using std::strlen;

    // GIVEN

    MemoryArea ma(1024);
    const char* TAG0 = "TAG0_long";
    const char* TAG1 = "TAG1";
    const size_t SIZE0 = sizeof(Field) + strlen(TAG0) + 1;  // +1 for terminating zero
    const size_t SIZE1 = SIZE0 + strlen(TAG1) + 1;

    Field* f0 = ma.create<Field>();
    f0->setTag(TAG0);
    BOOST_CHECK_EQUAL(ma.size(), SIZE0);

    // WHEN
    f0->setTag(TAG1);

    // THEN
    BOOST_CHECK_EQUAL(ma.size(), SIZE1);  // size DID NOT change
    BOOST_CHECK_EQUAL(f0->tag().c_str(), TAG1);
}

void Field_resetTagLong() {
    using fixparser::MemoryArea;
    using fixparser::Field;
    using std::strlen;

    // GIVEN

    MemoryArea ma(1024);
    const char* TAG0 = "TAG0";
    const char* TAG1 = "TAG1_long";
    const size_t MA_SIZE0 = sizeof(Field) + strlen(TAG0) + 1;  // +1 for terminating zero
    const size_t MA_SIZE1 = MA_SIZE0 + strlen(TAG1) + 1;  // TAG0 was NOT dealocatted

    Field* f0 = ma.create<Field>();
    f0->setTag(TAG0);
    BOOST_CHECK_EQUAL(ma.size(), MA_SIZE0);

    // WHEN
    f0->setTag(TAG1);

    // THEN
    BOOST_CHECK_EQUAL(ma.size(), MA_SIZE1);  // size DID change
    BOOST_CHECK_EQUAL(f0->tag().c_str(), TAG1);
}

void Field_resetTagLongThanShort() {
    using fixparser::MemoryArea;
    using fixparser::Field;
    using std::strlen;

    // GIVEN

    MemoryArea ma(1024);
    const char* TAG0 = "TAG0";
    const char* TAG1 = "TAG001_long";
    const char* TAG2 = "TAG02";
    const size_t SIZE0 = sizeof(Field) + strlen(TAG0) + 1;  // +1 for terminating zero
    const size_t SIZE1 = SIZE0 + strlen(TAG1) + 1;
    const size_t SIZE2 = SIZE1 + strlen(TAG2) + 1;

    Field* f0 = ma.create<Field>();
    f0->setTag(TAG0);
    BOOST_CHECK_EQUAL(ma.size(), SIZE0);
    f0->setTag(TAG1);
    BOOST_CHECK_EQUAL(ma.size(), SIZE1);

    // WHEN
    f0->setTag(TAG2);

    // THEN
    BOOST_CHECK_EQUAL(ma.size(), SIZE2);
    BOOST_CHECK_EQUAL(f0->tag().c_str(), TAG2);
}

void Field_setTagFromMemoryArea() {
    using fixparser::MemoryArea;
    using fixparser::Field;
    using std::strlen;

    // GIVEN

    MemoryArea ma(1024);
    const int MA_STR_CAPACITY = 32;
    const int SIZE = sizeof(Field) + MA_STR_CAPACITY;

    // WHEN

    char* ma_str = ma.allocate<char>(MA_STR_CAPACITY);
    fixparser::static_strncpy(ma_str, "just_test");

    Field* f0 = ma.create<Field>();
    f0->setTag(ma_str);

    // THEN
    BOOST_CHECK_EQUAL(ma.size(), SIZE);   // MemoryArea size did not change, ma_str is allocated inside the same MemoryArea
    BOOST_CHECK(ma_str == f0->tag().c_str());   // string was NOT copied, just set a pointer
    BOOST_CHECK_EQUAL(f0->tag().c_str(), "just_test");
}

void Field_writeTo() {
    using fixparser::MemoryArea;
    using fixparser::Field;
    using fixparser::static_strlen;
    using std::stringstream;

    // GIVEN

    MemoryArea ma(1024);
    Field* f = ma.create<Field>();
    f->setTag("8");
    f->setValue("FIX.4.2");
    stringstream ss;

    // WHEN
    f->writeTo(&ss);

    // THEN

    const size_t expectedSize = static_strlen("8=FIX.4.2|");
    BOOST_CHECK_EQUAL(ss.str().size(), expectedSize);
    BOOST_CHECK_EQUAL(ss.str().c_str()[expectedSize - 1], 0x01);

    stringstream expectedSs;
    expectedSs.write("8=FIX.4.2", static_strlen("8=FIX.4.2"));
    expectedSs.put(0x01);
    BOOST_CHECK_EQUAL(ss.str(), expectedSs.str());
//    LOG_INFO("ss: " << ss.str() << ", expected: "  << expectedSs.str());
}
